/**
 * huize Service Inc
 * All Rights Reserved @2018
 */
package com.huize.ladon.apm.common.convert;

import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.beans.BeanMap;
import net.sf.cglib.core.Converter;

import java.util.HashMap;
import java.util.Map;

/**
 * 描述: 默认POJO实现的Converter转换接口
 *
 * @author tianyuliang
 * @version $Id: PojoConverter.java, v0.1 2019/3/25
 */
@SuppressWarnings({"SameParameterValue", "unused", "unchecked"})
public interface PojoConverter {

    /**
     * 将目标对象转为{@code Map<属性名, 属性值>}
     *
     * @param obj 转换源
     * @return {@code Map<属性名, 属性值>}
     */
    static Map<String, Object> toMap(Object obj) {
        BeanMap beanMap = BeanMap.create(obj);
        Map<String, Object> map = new HashMap<>(beanMap.size());
        beanMap.forEach((key, val) -> map.put((String) key, val));
        return map;
    }

    /**
     * 将source拷贝到目标对象，仅拷贝属性名称且类型相同的字段。 <br>
     * 目标对象必须含有无参构造方法
     *
     * @param source      拷贝源（注意source为空时抛出NullPointerException异常）
     * @param targetClass 目标对象Class,必须含有无参构造方法
     * @return target对象
     * @throws NullPointerException source为空时抛出
     * @throws RuntimeException     产生异常时抛出
     */
    static <C> C copy(Object source, Class<C> targetClass) {
        return copy(source, targetClass, null);
    }

    /**
     * 将source复制到目标对象。 <br>
     * 目标对象必须含有无参构造方法
     *
     * @param source      拷贝源
     * @param targetClass 目标对象Class,必须含有无参构造方法
     * @param converter   转换器
     * @return target对象
     * @throws NullPointerException source为空时抛出
     * @throws RuntimeException     产生异常时抛出
     */
    static <C> C copy(Object source, Class<C> targetClass, Converter converter) {
        if (source == null) {
            throw new NullPointerException("source is null.");
        }

        boolean useConverter = converter != null;
        try {
            BeanCopier copier = BeanCopier.create(source.getClass(), targetClass, useConverter);
            C c = targetClass.newInstance();
            // 实际执行属性拷贝
            copier.copy(source, c, converter);
            return c;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将当前对象转换为目标对象。仅转换名称且类型相同的字段<br>
     * 如果需要转换字段不同或字段类型不同的情况,
     * 请使用<code>convert(Class<T> clazz, Converter converter)</code>
     *
     * @param clazz 目标对象class，必须含有无参构造方法
     * @return 目标对象
     * @throws RuntimeException 产生异常时抛出
     */
    default <T> T convert(Class<T> clazz) {
        return convert(clazz, null);
    }

    /**
     * 将当前对象转换为目标对象。<br>
     * 如果需要转换字段不同或字段类型不同的情况，则使用converter
     *
     * @param clazz     目标对象class，必须含有无参构造方法
     * @param converter 转换器
     * @return 目标对象
     * @throws RuntimeException 产生异常时抛出
     */
    default <T> T convert(Class<T> clazz, Converter converter) {
        boolean useConverter = converter != null;
        try {
            BeanCopier copier = BeanCopier.create(this.getClass(), clazz, useConverter);
            T t = clazz.newInstance();
            // 实际执行属性拷贝
            copier.copy(this, t, converter);
            return t;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从目标对象拷贝名称且类型相同的字段的值。<br>
     * 如果需要转换字段不同或字段类型不同的情况,
     * 请使用 {@code copyFrom(Object obj, Converter converter)}
     *
     * @param obj 拷贝源
     */
    default void copyFrom(Object obj) {
        copyFrom(obj, null);
    }

    /**
     * 从目标对象拷贝属性值<br>
     * 如果需要转换字段不同或字段类型不同的情况，则使用converter
     *
     * @param obj       拷贝源
     * @param converter 转换器
     */
    default void copyFrom(Object obj, Converter converter) {
        if (obj == null) {
            return;
        }

        try {
            boolean useConverter = converter != null;
            BeanCopier copier = BeanCopier.create(obj.getClass(), this.getClass(), useConverter);
            copier.copy(obj, this, converter);
        } catch (Throwable e) {
            System.out.println("copy from obj error." + e.getMessage());
            throw e;
        }
    }

    /**
     * 将当前对象转为Map<属性名, 属性值>
     *
     * @return map
     */
    default Map<String, Object> toMap() {
        return toMap(this);
    }

}

